梦入琼楼寒有月,行过石树冻无烟

Spring Boot 条件与其实现

我们可以通过直接阅读引入org.springframework.boot.autoconfigure.aop.AopAutoConfiguration;的Aop包下的AopAutoConfiguration.class来了解到其Spring boot自动配置的实现过程,而AopAutoConfigutaion.class中的关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Advice.class)
static class AspectJAutoProxyingConfiguration {

@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
matchIfMissing = false)
static class JdkDynamicAutoProxyConfiguration {

}

@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class CglibAutoProxyConfiguration {

}

}

通过AopAutoConfiguration.class的代码中我们可以理解到,基本上Spring Boot中的自动配置实现过程是通过@Conditational而@ConditationalOnProperty(即条件属性,单Conditational指“有条件的”)注解实现的,我们可通过Java Doc的官方文档中读取和了解到有关@ConditationalOnPropertry注解相关的信息

1.1. 条件注解

Spring的条件注解说白了就是程序的配置类和配置项,即满足特定了条件才会被调用,这就和if和try的条件判断语句一样,if的存在是判断是否等于或不等于某个条件,而try的存在则是判断这段code是否出现异常,出现异常后又执行什么方法或调用什么类等,在这里Spirng条件注解同样的意思。
spring-boot-autoconfigure-2.4.1.jarspring boot核心依赖包下,有38个条件注解类如下:

ID DA FA
@ConditionalOnBean OnBeanCodition.class Spring存在指定的实例Bean
@ConditionalOnClass OnClassCondition.clss 类加载器中存在对应的类
@ConditionalOnCloudPlatform OnCloudPlatformCondition.class 类中是否存在云平台
@ConditionalOnExpression OnExpressionCondition.clss 判断SpEL表达式是否成立
@ConditionalOnJava OnJavaCondition.class Java版本是否符合你的条件要求
@ConditionalOnJndi OnJndiCondition.class 在JNDI(即Java命名项目和目录接口)条件查找指定位置
@ConditionalOnMissingBean OnBeanCondition.class Spring容器中不存在指定的实例
@ConditionalOnMissingClass OnClassCondition.class 类加载器中不存在对应的类
@ConditionalOnNotWebApplication OnWebApplicationCondition.class 当前应用程序不是Web程序应用
@ConditionalOnProperty OnResourceCondition.class 应用环境中实行是否存在指定的值
@ConditionalOnResource OnResourceCondition.class 是否存在指定的资源文件
@ConditionalOnSingleCandidate OnBeanCondition.class Spring容器中是否存在且只存在一个对应的实例Bean
@ConditionalOnWebApplication OnWebApplicationCondition.class 当前应用程序是Web程序

通过阅读以上列出的相关注解和注解实现类我们可随即查看实现类的过程,而本次我们主要阅读```OnWebApplicationCondition.class``即@ConditionalOnWebApplication的注解实现方法以及原理。

1.1.1. OnWebApplicationCondition.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

// 匹配Web应用
@Override
protected ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
for (int i = 0; i < outcomes.length; i++) {
String autoConfigurationClass = autoConfigurationClasses[i];
if (autoConfigurationClass != null) {
outcomes[i] = getOutcome(
autoConfigurationMetadata.get(autoConfigurationClass, "ConditionalOnWebApplication"));
}
}
return outcomes;
}

// 确定匹配结果并以日志的形式输出
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 检查是否使用@OnWebApplicationCndition注解
boolean required = metadata.isAnnotated(ConditionalOnWebApplication.class.getName());
// isWebApplication 检测是否是Web 应用程序
ConditionOutcome outcome = isWebApplication(context, metadata, required);
// 如果有注解但不是Web应用环境则返回true
if (required && !outcome.isMatch()) {
return ConditionOutcome.noMatch(outcome.getConditionMessage());
}
// 如果没有注解但是web应用环境则返回true
if (!required && outcome.isMatch()) {
return ConditionOutcome.noMatch(outcome.getConditionMessage());
}
// 如果使用了@ConditionalOnWebApplication注解并是Web应用则返回匹配
return ConditionOutcome.match(outcome.getConditionMessage());
}

// 是否为Web应用环境
private ConditionOutcome isAnyWebApplication(ConditionContext context, boolean required) {
ConditionMessage.Builder message = ConditionMessage.forCondition(ConditionalOnWebApplication.class,
required ? "(required)" : "");
ConditionOutcome servletOutcome = isServletWebApplication(context);
if (servletOutcome.isMatch() && required) {
return new ConditionOutcome(servletOutcome.isMatch(), message.because(servletOutcome.getMessage()));
}
ConditionOutcome reactiveOutcome = isReactiveWebApplication(context);
if (reactiveOutcome.isMatch() && required) {
return new ConditionOutcome(reactiveOutcome.isMatch(), message.because(reactiveOutcome.getMessage()));
}
return new ConditionOutcome(servletOutcome.isMatch() || reactiveOutcome.isMatch(),
message.because(servletOutcome.getMessage()).append("and").append(reactiveOutcome.getMessage()));
}

自定义条件

条件实现类

MyCondition.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.example.demo;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;


public class MyCondition implements Condition {

@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO Auto-generated method stub
// 存在 test.properties
return context.getResourceLoader().getResource("classpath:test.properties").exists();
}
}

YourCondition.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.example.demo;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class YourCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO Auto-generated method stub
// 不存在 test.properties
return !context.getResourceLoader().getResource("classpath:test.properties").exists();
}
}

Bean 接口实现类

MessagePrint.java

1
2
3
4
package com.example.demo;
public interface MessagePrint {
public String showMessage();
}

MyMessagePrint.java

1
2
3
4
5
6
7
8
package com.example.demo;

public class MyMessagePrint implements MessagePrint {
@Override
public String showMessage() {
return "test.properties 文件存在";
}
}

YourMessagePrint.java

1
2
3
4
5
6
7
8
9
package com.example.demo;

public class YourMessagePrint implements MessagePrint {
@Override
public String showMessage() {
// TODO Auto-generated method stub
return "test.properties 文件不存在";
}
}

配置类

ConditionConfig.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.example.demo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Conditional;

@Configuration
public class ConditionConfig {
@Bean
@Conditional(MyCondition.class)
public MyMessagePrint myMessage() {
return new MyMessagePrint();
}

@Bean
@Conditional(YourCondition.class)
public MessagePrint yourmessage() {
return new YourMessagePrint();
}
}

测试类

TestMain.java

1
2
3
4
5
6
7
8
9
10
11
12
package com.example.demo;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestMain {
private static AnnotationConfigApplicationContext contextapplication;
public static void main (String args[]) {
contextapplication = new AnnotationConfigApplicationContext(ConditionConfig.class);
MessagePrint message = contextapplication.getBean(MessagePrint.class);
System.out.println(message.showMessage());
}
}
⬅️ Go back